package org.vacoor.nothing.web.service.support;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Maps;
import net.sf.cglib.beans.BeanMap;
import org.vacoor.nothing.web.dao.CrudDao;
import org.vacoor.nothing.web.entity.Auditable;
import org.vacoor.nothing.web.entity.Idable;
import org.vacoor.nothing.web.entity.LogicDeletable;
import org.vacoor.nothing.web.entity.UuidEntity;
import org.vacoor.nothing.web.util.DiffSwitch;

import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;

/**
 */
public class GeneralServiceImpl<E extends Idable<ID>, ID extends Serializable> extends CrudServiceImpl<E, ID> {
    private String[] DEFAULT_UPDATE_IGNORE_PROPERTIES = {"id"};

    private String[] updateIgnoreProperties = DEFAULT_UPDATE_IGNORE_PROPERTIES;

    @Override
    public void save(E e) {
        doInternalSave(getCrudDao(), e);
    }

    @Override
    public void update(E e) {
        doInternalUpdate(getCrudDao(), e);
    }

    public E update(E e, String... ignoreProperties) {
        updatePartial(e, Predicates.not(Predicates.in(Arrays.asList(ignoreProperties))), Predicates.alwaysTrue());
        return e;
    }


    @SuppressWarnings("unchecked")
    public void updatePartial(E e, Predicate<String> pPredicate, Predicate<?> vPredicate) {
        final ID id = e.getId();

        Map<String, Object> patch = BeanMap.create(e);

        // 过滤符合要求的属性
        Predicate<String> keyPredicate = Predicates.not(Predicates.in(Arrays.asList(updateIgnoreProperties)));
        if (null != pPredicate) {
            keyPredicate = Predicates.and(keyPredicate, pPredicate);
        }
        patch = Maps.filterKeys(patch, keyPredicate);

        // 过滤值符合要求的属性
        if (null != vPredicate) {
            patch = Maps.filterValues(patch, (Predicate<Object>) vPredicate);
        }

        // 没有任何内容需要更新
        if (1 > patch.size()) {
            // LOG.debug("");
            return;
        }

        applyPatch(id, patch);
    }

    public <T, ID> T applyPatch(ID id, Map<String, ?> patchData) {
        for (Map.Entry<String, ?> entry : patchData.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

        // E managed = find(id);
        // BeanMap.create(managed).putAll(patchData);
        // TODO update managed

        return null;
    }

    @Override
    public void delete(E e) {
        doInternalDelete(getCrudDao(), e);
    }


    /* ****************
     *
     * ****************/

    protected <V extends Idable<I>, I extends Serializable> void doInternalSave(CrudDao<V, I> dao, V e) {
        // 如果实现审计接口
        if (Auditable.class.isAssignableFrom(e.getClass())) {
            Auditable audit = (Auditable) e;
            audit.setCreatedBy(null);
            audit.setCreatedDate(new Date());
            audit.setLastModifiedBy(null);  // TODO
            audit.setLastModifiedDate(new Date());
        }
        dao.save(e);
    }


    protected <V extends Idable<I>, I extends Serializable> void doInternalUpdate(CrudDao<V, I> dao, V e) {
        I id = null != e ? e.getId() : null;
        if (null == id) {
            throw new IllegalArgumentException("");
        }
        if (Auditable.class.isAssignableFrom(e.getClass())) {
            Auditable audit = (Auditable) e;
            audit.setLastModifiedBy(null);
            audit.setLastModifiedDate(new Date());
        }
        dao.update(e);
    }

    protected <V extends Idable<I>, I extends Serializable> void doInternalDelete(CrudDao<V, I> dao, V e) {
        if (LogicDeletable.class.isAssignableFrom(e.getClass())) {
            e = dao.find(e.getId());
            ((LogicDeletable) e).setDeleted(true);

            doInternalUpdate(dao, e);
        } else {
            dao.delete(e);
        }
    }


    // ----------------------------------------------

    /* *************************
     *
     * *********************** */

    protected class RelationShipDiffSwitch<T extends Idable<?>> extends DiffSwitch<T, T> {
        @Override
        protected boolean doEquals(T left, T right) {
            if (null == right || null == right.getId()) {
                throw new IllegalStateException("managed entity id must be not null");
            }
            // return left == right || null != left && super.doDefaultEquals(BeanMap.create(left).get("id"), BeanMap.create(right).get("id"));
            return left == right || null != left && super.doDefaultEquals(left.getId(), right.getId());
        }
    }

    static class X extends UuidEntity {
        private String name;
        private String age;

        @Override
        public String getId() {
            return super.getId();
        }

        @Override
        public void setId(String id) {
            super.setId(id);
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAge() {
            return age;
        }

        public void setAge(String age) {
            this.age = age;
        }
    }

    public static void main(String[] args) throws IOException {
        GeneralServiceImpl<X, String> generalService = new GeneralServiceImpl<X, String>() {
        };
        X x = new X();
        x.setId("123");
        x.age = "age";
        x.name = "name";
        generalService.update(x, "age");
        /*
        Properties props = new Properties(System.getProperties());
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("cg.properties");
        if (null != is) {
            try {
                props.load(is);
            } catch (IOException e) { }
        }

        ReloadableResourceBundleMessageSource rbms = new ReloadableResourceBundleMessageSource();
        rbms.setBasename("messages");
        rbms.setFallbackToSystemLocale(true);
        rbms.setUseCodeAsDefaultMessage(true);

        String prop = props.getProperty("nls.code.start");
        AtomicInteger startCode = new AtomicInteger(StringUtils.hasText(prop) ? Integer.parseInt(prop) : 100000);
        for (int i = 0; i < 100; i++) {
            int code = startCode.incrementAndGet();
            String message = rbms.getMessage(String.valueOf(code), new String[0], Locale.getDefault());
            System.out.println(message);
        }

        URL resource = Thread.currentThread().getContextClassLoader().getResource(".");
        System.out.println(resource);

//        new JavaDocBuilder()
        props.setProperty("nls.code.start", String.valueOf(startCode.incrementAndGet()));
        props.store(System.out, "hehe");
        */
    }
}
